package com.zzyl.intercept;


import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.zzyl.utils.MgtThreadLocal;
import com.zzyl.utils.MobThreadLocal;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.List;

/**
 * @Intercepts：mybatis的拦截器
 * @Signature：指明该拦截器需要拦截哪个接口的哪一个方法，type接口类，method方法名称，args方法参数类型
 */
@Component
@Intercepts(@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class}))
public class AutoFillInterceptor implements Interceptor {

    private static final String CREATE_BY = "createBy";
    private static final String UPDATE_BY = "updateBy";

    private static final String CREATE_TIME = "createTime";
    private static final String UPDATE_TIME = "updateTime";

    //进行拦截的时候要执行的方法
    @Override
    @SuppressWarnings("all")
    public Object intercept(Invocation invocation) throws Throwable {
        // 获取描述SQL语句的映射信息
        MappedStatement ms = (MappedStatement) invocation.getArgs()[0];
        // 获取sql参数实体 ParamMap
        Object parameter = invocation.getArgs()[1];

        //获取SQL的类型，INSERT？UPDATE？
        SqlCommandType sqlCommandType = ms.getSqlCommandType();
        if (ObjectUtil.isEmpty(parameter) || ObjectUtil.isEmpty(sqlCommandType)) {
            return invocation.proceed();// 继续执行原始方法
        }

        // 获取用户ID
        Long userId = loadUserId();

        if (ObjectUtil.equals(SqlCommandType.INSERT, sqlCommandType)) {
            // 插入操作
            if (parameter instanceof MapperMethod.ParamMap) {
                // 批量插入的情况
                MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) parameter;
                List list = (List) paramMap.get("list");
                list.forEach(val -> {
                    // 设置创建人和创建时间字段值
                    setFieldValByName(CREATE_BY, userId, val);
                    setFieldValByName(UPDATE_BY, userId, val);

                    setFieldValByName(CREATE_TIME, LocalDateTime.now(), val);
                    setFieldValByName(UPDATE_TIME, LocalDateTime.now(), val);
                });
                paramMap.put("list", list);
            } else {
                // 单条插入的情况：设置创建人和创建时间字段值
                setFieldValByName(CREATE_BY, userId, parameter);
                setFieldValByName(UPDATE_BY, userId, parameter);

                setFieldValByName(CREATE_TIME, LocalDateTime.now(), parameter);
                setFieldValByName(UPDATE_TIME, LocalDateTime.now(), parameter);
            }
        } else if (ObjectUtil.equals(SqlCommandType.UPDATE, sqlCommandType)) {
            // 更新操作：设置更新人和更新时间字段值
            setFieldValByName(UPDATE_BY, userId, parameter);
            setFieldValByName(UPDATE_TIME, LocalDateTime.now(), parameter);
        }
        // 继续执行原始方法
        return invocation.proceed();
    }

    /**
     * 通过反射设置实体的字段值
     *
     * @param fieldName 字段名
     * @param fieldVal  字段值
     * @param parameter 实体对象
     */
    private void setFieldValByName(String fieldName, Object fieldVal, Object parameter) {
        MetaObject metaObject = SystemMetaObject.forObject(parameter);
        if (StrUtil.equals(fieldName, CREATE_BY)) {
            Object value = metaObject.getValue(fieldName);
            //有值，则不自动填充
            if (ObjectUtil.isNotEmpty(value)) {
                return;
            }
        }

        if (metaObject.hasSetter(fieldName)) {
            metaObject.setValue(fieldName, fieldVal);
        }
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof Executor) {
            // 对目标对象进行包装，返回代理对象
            return Plugin.wrap(target, this);
        }
        // 非 Executor 类型的对象，直接返回原始对象
        return target;
    }


    /**
     * 获取当前用户的ID，用于填充创建人和更新人字段的值
     *
     * @return 当前用户ID
     */
    public static Long loadUserId() {
        // 从 MgtThreadLocal 中获取用户ID
        Long userId = MgtThreadLocal.getUserId();
        if (ObjectUtil.isNotEmpty(userId)) {
            return userId;
        }

        // 从 MobThreadLocal 中获取用户ID
        userId = MobThreadLocal.getUserId();
        if (ObjectUtil.isNotEmpty(userId)) {
            return userId;
        }

        // 如果管理端、移动端线程中都没有，则默认值 1671403256519078138
        return 1671403256519078138L;
    }
}

